package com.csw.spring.context;

import com.csw.spring.anno.*;
import com.csw.spring.framework.BeanDefinition;
import com.csw.spring.framework.BeanNameAware;
import com.csw.spring.framework.InitializingBean;
import com.sun.tools.javac.util.StringUtils;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author  csw
 * @date  2021/12/19 01:12:45
 * @version 1.0
 */
public class CswApplicationContext {

    private Class configClass;

    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>(); //存放解析后的bean缓存池
    private Map<String,Object> singletonObj = new HashMap<String, Object>(); //单例池

    public CswApplicationContext(Class configClass) {
        this.configClass = configClass;

        //扫描 --得到BeanDefinition对象
        scan(configClass, beanDefinitionMap);

        //创建非懒加载的单例bean
        createNonLazySingleton(beanDefinitionMap);
    }

    private  void createNonLazySingleton(Map<String, BeanDefinition> beanDefinitionMap) {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals("singleton") && !beanDefinition.isLazy()) {
                //创建bean
                Object valueBean = createBean(beanDefinition,beanName);
                singletonObj.put(beanName,valueBean);
            }
        }
    }


    private Object createBean(BeanDefinition beanDefinition,String beanName) {
        //通过反射获取到bean对象
        Class beanClass = beanDefinition.getBeanClass();
        //getDeclaredConstructor:获取到无参到构造方法
        try {
            Object newInstance = beanClass.getDeclaredConstructor().newInstance();
            //填充属性 ->  依赖注入
            for (Field declaredField : beanClass.getDeclaredFields()) {

                if(declaredField.isAnnotationPresent(Autowired.class)) {
                    //byType....
                    //byName
                    Object bean = getBean(declaredField.getName());
                    declaredField.setAccessible(true);
                    declaredField.set(newInstance,bean);
                }
            }
            //判断有没有实现BeanNameAware接口
            if(newInstance instanceof BeanNameAware) {
                //回调
                ((BeanNameAware) newInstance).setBeanName(beanName);
            }
            //判断有没有实现InitializingBean接口
            if(newInstance instanceof InitializingBean) {
                ((InitializingBean)newInstance).afterPropertiesSet();
            }
            return newInstance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void scan(Class configClass, Map<String, BeanDefinition> beanDefinitionMap) {
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            System.out.println("开始扫描");
            //获取ComponentScan的信息
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value();//获取扫描路径,com.csw.spring.service.impl
            //替换com.csw.spring.service.impl为com/csw/spring/service/impl
            path = path.replace(".","/");

            //获取类加载后文件路径
            ClassLoader classLoader = CswApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            System.out.println("根据类加载获取到完整路径：" + resource);
            File file = new File(resource.getFile());

            //对获取对文件路径进行循环遍历
            for (File f : file.listFiles()) {

                //获取path路径：/Users/shengwencheng/Desktop/cloud/SpringCore/spring-core/Spring/target/classes/com/csw/spring/service/impl/UserServiceImpl.class
                String s = f.getAbsolutePath();
                //只要class结尾的文件
                if(s.endsWith(".class")) {
                    //截取com到.class之间到路径
                    s = s.substring(s.indexOf("com"),s.indexOf(".class"));
                    //把转义替换成.
                    s = s.replace("/",".");
                    System.out.println("通过字符串截取和转义替换或取到的类：" + s);
                    try {
                        Class  clazz = classLoader.loadClass(s);
                        //判断类上是否有Component注解
                        if (clazz.isAnnotationPresent(Component.class)) {

                            BeanDefinition beanDefinition = new BeanDefinition();
                            //声明bean类型
                            beanDefinition.setBeanClass(clazz);

                            Component componentAnnotation = (Component) clazz.getAnnotation(Component.class);
                            System.out.println("获取到加了Component注解到类：" + componentAnnotation);
                            String beanName = componentAnnotation.value();
                            System.out.println("获取Component注解里面的value：" + beanName);
                            //判断是否是非懒加载的bean
                            if (clazz.isAnnotationPresent(Lazy.class)) {
                                beanDefinition.setLazy(true);
                            }
                            //判断是否是原型bean
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);
                            } else {
                                //单例
                                beanDefinition.setScope("singleton");
                            }
                            //这里包括了项目中所有bean的定义，根据beanName在缓存里面去查找有没有传入的这个bean对象
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public Object getBean(String beanName) {
        if(!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException();
        } else {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            System.out.println("存放解析后的bean缓存池:" + beanDefinition.getScope());
            //判断是原型bean还是单例bean
            if(beanDefinition.getScope().equals("prototype")) {
                //创建一个bean
                Object bean = createBean(beanDefinition,beanName);
                return bean;
            } else if(beanDefinition.getScope().equals("singleton")) {
                //单例池里面获取单例bean
                Object o = singletonObj.get(beanName);
                if(o == null) {
                    Object bean = createBean(beanDefinition,beanName);
                    singletonObj.put(beanName,bean);
                }
                return o;
            }
        }
        return null;
    }
}
